/*
 * CallServlet.java
 *
 * Created on January 17, 2008, 12:04 PM
 */
package clicktodial.sip;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.sip.B2buaHelper;
import javax.servlet.sip.SipApplicationSession;
import javax.servlet.sip.SipFactory;
import javax.servlet.sip.SipServlet;
import javax.servlet.sip.SipServletRequest;
import javax.servlet.sip.SipServletResponse;
import javax.servlet.sip.SipSession;
import javax.servlet.sip.SipSessionEvent;
import javax.servlet.sip.SipSessionListener;
import javax.servlet.sip.annotation.SipListener;



/**
 * @author jp
 */
@javax.servlet.sip.annotation.SipServlet
@SipListener
public class CallSipServlet extends SipServlet implements SipSessionListener {

    private static final Logger logger = 
            Logger.getLogger(CallSipServlet.class.getName());
    
    // inject the SipFactory
    @Resource
    private SipFactory sf;


    @Override
    protected void doSuccessResponse(SipServletResponse resp)
            throws ServletException, IOException {
        logger.info("Received a response.\n" + resp);

        if (resp.getMethod().equals("INVITE")) {
            List<SipSession> sipSessions = getSipSessions(resp.getApplicationSession());
            if (sipSessions.size() == 1) {
                sipSessions.get(0).setAttribute("ACK", resp.createAck());
                sendInviteToClient(resp);
            } else { // 200 OK from Client
                sendAckToClient(resp);
                sendAckToServer(resp);
            }
        } else if (resp.getMethod().equals("BYE")) {
            send200OKToClient(resp);
        }
    }

    @Override
    protected void doErrorResponse(SipServletResponse resp) throws ServletException, IOException {
        logger.info("Received an error response.\n" + resp);
        resp.getSession().getApplicationSession().invalidate();
    }

    @Override
    protected void doRequest(SipServletRequest clientBye) throws ServletException, IOException {
        logger.info("Received a request.\n" + clientBye);
        if (clientBye.getMethod().equals("BYE")) {
            clientBye.getSession().setAttribute("BYE",
                    clientBye.createResponse(200));
            sendByeToServer(clientBye);
        }
    }

    private void sendInviteToClient(SipServletResponse serverResp)
            throws ServletException, IOException {
        SipServletRequest serverReq = serverResp.getRequest();
        B2buaHelper b2buaHelper = serverReq.getB2buaHelper();

        // Swap To & From headers.
        Map<String, List<String>> headerMap = new HashMap<String, List<String>>();
        List<String> from = new ArrayList<String>();
        from.add(serverResp.getHeader("From"));
        headerMap.put("To", from);
        List<String> to = new ArrayList<String>();
        to.add(serverResp.getHeader("To"));
        headerMap.put("From", to);

        SipServletRequest clientRequest = b2buaHelper.createRequest(serverReq, true, headerMap);
        clientRequest.setRequestURI(clientRequest.getAddressHeader("To").getURI());
        if (serverResp.getContent() != null) { // set sdp1
            clientRequest.setContent(serverResp.getContent(),
                    serverResp.getContentType());
        }
        logger.info("Sending INVITE to client.\n" + clientRequest);
        clientRequest.send();
    }

    private void sendAckToClient(SipServletResponse clientResp) throws ServletException, IOException {
        SipServletRequest ack = clientResp.createAck();
        logger.info("Sending ACK to client.\n" + ack);
        ack.send();
    }

    private void sendAckToServer(SipServletResponse clientResp) throws ServletException, IOException {
        B2buaHelper b2buaHelper = clientResp.getRequest().getB2buaHelper();
        SipSession clientSession = clientResp.getSession();
        SipSession serverSession = b2buaHelper.getLinkedSession(clientSession);
        SipServletRequest ack = (SipServletRequest) serverSession.getAttribute("ACK");
        serverSession.removeAttribute("ACK");
        if (clientResp.getContent() != null) { // set sdp2
            ack.setContent(clientResp.getContent(), clientResp.getContentType());
        }
        logger.info("Sending ACK to server.\n" + ack);
        ack.send();
    }

    private void sendByeToServer(SipServletRequest clientBye) throws ServletException, IOException {
        B2buaHelper b2buaHelper = clientBye.getB2buaHelper();
        SipSession serverSession = b2buaHelper.getLinkedSession(clientBye.getSession());
        SipServletRequest serverBye = serverSession.createRequest("BYE");
        logger.info("Sending BYE request.\n" + serverBye);
        serverBye.send();
    }

    private void send200OKToClient(SipServletResponse serverResp) throws ServletException, IOException {
        B2buaHelper b2buaHelper = serverResp.getRequest().getB2buaHelper();
        SipSession clientSession = b2buaHelper.getLinkedSession(serverResp.getSession());
        SipServletResponse clientResp = (SipServletResponse) clientSession.getAttribute("BYE");
        clientSession.removeAttribute("BYE");
        clientResp.setStatus(serverResp.getStatus(), serverResp.getReasonPhrase());
        logger.info("Sending response.\n" + clientResp);
        clientResp.send();
        clientSession.getApplicationSession().invalidate();
    }

    private List<SipSession> getSipSessions(SipApplicationSession sas) {
        List<SipSession> sipSessions = new ArrayList<SipSession>();
        Iterator<SipSession> iter = (Iterator<SipSession>) sas.getSessions("SIP");
        while (iter.hasNext()) {
            sipSessions.add(iter.next());
        }
        return sipSessions;
    }


   
    ////////
    // SipSessionListener interface
    
    /**
     * Called when a session is created.  Ignore.
     * @param sse the event
     */    
    public void sessionCreated(SipSessionEvent sse) {
        logger.info("Session created");
    }

    /**
     * Called when a session is removed.  Remove any call data associated
     * with the given session.
     * @param sse the event
     */
    public void sessionDestroyed(SipSessionEvent sse) {
        logger.info("Session destroyed");
    }
    
    public void sessionReadyToInvalidate(SipSessionEvent sse) {
        logger.info("Session ready to be invalidated");
    }

}
